$import( "SmitearWebGL.Programs.AbstractProgram" );
$import( "SmitearWebGL.Resources.MaterialLayer" );

SmitearWebGL.Programs.LightPrePass.MaterialProgram = SmitearWebGL.Programs.AbstractProgram.extend(
{
	/******** constructor ********/
	
	init : function()
	{
		this._super();
		this._registClass( "SmitearWebGL.Programs.LightPrePass.MaterialProgram" );
		
		this.diffMap = null;
		this.specMap = null;
		this.aoMap = null;
	}
	,
	
	/******** public method ********/
	
	activeUniform : function( renderableObject )
	{
		var hasMapToDiffuse = false;
		var hasMapToSpecular = false;
		var hasMapToEmissive = false;
		var hasMapToEnv = false;
		var material = renderableObject.getMaterial();
		var layers = material.getLayers();
		
		for ( var i = 0; i < layers.length; i ++ )
		{
			var layer = material._layers[i];
			var texture = layer.getTexture();
	
			if ( texture && texture.getGlTexture() )
			{
				if ( layer.mapinput == SmitearWebGL.Resources.MaterialLayer.MAP_ENV )
					hasMapToEnv = true;
				
				layer.glUniform( SmitearWebGL.ShaderUniform.layerMatrix, this._shader );
	
				this.context.gl.activeTexture( this.context.gl.TEXTURE0 + i );
				this.context.gl.bindTexture( texture.getGlTextureTarget(), texture.getGlTexture() );
				texture.glUniform( SmitearWebGL.ShaderUniform.texture, this._shader, i );
			}
			
			if ( layer.isMapToDiffuse() ) hasMapToDiffuse = true;
			if ( layer.isMapToSpecular() ) hasMapToSpecular = true;
			if ( layer.isMapToEmissive() ) hasMapToEmissive = true;
		}
		
		material.glUniform( SmitearWebGL.ShaderUniform.ambient, this._shader );

		if ( ! hasMapToDiffuse ) material.glUniform( SmitearWebGL.ShaderUniform.diffuse, this._shader );
		if ( ! hasMapToSpecular ) material.glUniform( SmitearWebGL.ShaderUniform.specular, this._shader );
		if ( ! hasMapToEmissive ) material.glUniform( SmitearWebGL.ShaderUniform.emissive, this._shader );
		if ( hasMapToEnv )
		{
			renderableObject.glUniform( SmitearWebGL.ShaderUniform.normalViewMatrix, this._shader );
			renderableObject._invWorldViewMatrix.n41 = renderableObject._invWorldViewMatrix.n42 = renderableObject._invWorldViewMatrix.n43 = 0.0;
		}
		
		renderableObject.glUniform( SmitearWebGL.ShaderUniform.projectionMatrix, this._shader );
		renderableObject.glUniform( SmitearWebGL.ShaderUniform.worldViewMatrix, this._shader );
		
		this.context.gl.uniform3fv( this._shader.uniforms.uAmbientColor.location, new Float32Array( renderableObject.getScene().getAmbient().flatten3v() ) );
		this.context.gl.uniform1f( this._shader.uniforms.uWndWidth.location, this.context.viewWidth - 1.0 );
		this.context.gl.uniform1f( this._shader.uniforms.uWndHeight.location, this.context.viewHeight - 1.0 );
		
		var diffMap = renderableObject.extra.diffMap;
		var specMap = renderableObject.extra.specMap;
		var aoMap = renderableObject.extra.aoMap;
		
		if ( diffMap )
		{
			this.context.gl.activeTexture( this.context.gl.TEXTURE0 + i + 0 );
			this.context.gl.bindTexture( diffMap.getGlTextureTarget(), diffMap.getGlTexture() );
			this.context.gl.uniform1i( this._shader.uniforms.uDiffMap.location, i );
		}
		
		if ( specMap )
		{
			this.context.gl.activeTexture( this.context.gl.TEXTURE0 + i + 1 );
			this.context.gl.bindTexture( specMap.getGlTextureTarget(), specMap.getGlTexture() );
			this.context.gl.uniform1i( this._shader.uniforms.uSpecMap.location, i + 1 );
		}
		
		if ( aoMap )
		{
			this.context.gl.activeTexture( this.context.gl.TEXTURE0 + i + 2 );
			this.context.gl.bindTexture( aoMap.getGlTextureTarget(), aoMap.getGlTexture() );
			this.context.gl.uniform1i( this._shader.uniforms.uAOMap.location, i + 2 );
		}
		
		return SmitearWebGL.Core.HResult.SUCCESS;
	}
	,
	
	/******** private method ********/
	
	_generateVertexShaderStr : function( renderableObject )
	{
		this._super();
		
		var vertStr = [];
		var material = renderableObject.getMaterial();
		var layers = material.getLayers();
		
		vertStr.push( "uniform mat4 uWorldViewMatrix;\n" );
		vertStr.push( "uniform mat4 uProjectionMatrix;\n" );
		vertStr.push( "uniform mat4 uNormalViewMatrix;\n" );
		
		if ( layers.length == 0 )
		{
			vertStr.push( "attribute vec3 " + SmitearWebGL.Resources.Buffer.AttribLocation.POSITION +";\n" );
			vertStr.push( "void main(void)\n" );
			vertStr.push( "{\n" );
			vertStr.push( "vec3 pos = (uWorldViewMatrix * vec4(" + SmitearWebGL.Resources.Buffer.AttribLocation.POSITION + ", 1.0)).xyz;\n" );
			vertStr.push( "gl_Position = uProjectionMatrix * vec4(pos, 1.0);\n" );
			vertStr.push( "}\n" );
		}
		else
		{
			var vertexBufferDesc = renderableObject.getVertexBuffer().getElementDescriptions();
			for ( var i = 0; i < vertexBufferDesc.length; ++ i )
			{
				if ( vertexBufferDesc[i].itemSize > 1 )
					vertStr.push( "attribute vec" + vertexBufferDesc[i].itemSize + " " + vertexBufferDesc[i].shaderAttributeLocation + ";\n" );
				else
					vertStr.push( "attribute float " + vertexBufferDesc[i].shaderAttributeLocation + ";\n" );
			}
			
			vertStr.push( material.getUniformStrings().vs );
			
			vertStr.push( "void main(void)\n" );
			vertStr.push( "{\n" );
			vertStr.push( "vec3 pos = (uWorldViewMatrix * vec4(" + SmitearWebGL.Resources.Buffer.AttribLocation.POSITION + ", 1.0)).xyz;\n" );
			vertStr.push( "gl_Position = uProjectionMatrix * vec4(pos, 1.0);\n" );
			vertStr.push( "vec3 texturePos = " + SmitearWebGL.Resources.Buffer.AttribLocation.TEXCOORD + ";\n" );
			vertStr.push( "vec3 eyevec = - normalize(pos);\n" );
			vertStr.push( "vec3 normal = normalize((uNormalViewMatrix * vec4(" + SmitearWebGL.Resources.Buffer.AttribLocation.NORMAL + ", 0.0)).xyz);\n" );
			
			vertStr.push( material.getImplementStrings().vs );
			
			vertStr.push( "}\n" );
		}
		
		var str = vertStr.join( "" );
		
		return str;
	}
	,
	_generateFragmentShaderStr : function( renderableObject )
	{
		this._super();
		
		var fragStr = [];
		var material = renderableObject.getMaterial();
		
		fragStr.push( "#ifdef GL_ES\n" );
		fragStr.push( "precision highp float;\n" );
		fragStr.push( "#endif\n" );
		fragStr.push( material.getUniformStrings().fs );
		fragStr.push( "void main(void)\n" );
		fragStr.push( "{\n" );
		fragStr.push( material.getImplementStrings().fs );
		fragStr.push( "}\n" );
		
		var str = fragStr.join( "" );
		
		return str;
	}
}
);